#include "Codec.h"
#include <memory>
#include "DSTHType.h"
#include "TcpSocketOps.h"

using namespace dsth;

static const size_t kMinMsgSize = 8;

// module of char message:
// | int32_t msg len   |
// |___________________|
// | to: string   \r\n |
// |___________________|
// | from: string \r\n |
// |___________________|
// | text: string      |
// |___________________|
message_t* Codec::Parse(const char* data, size_t len) {
    string buf(data, len);
    size_t to_pos = buf.find_first_of("\r\n");
    if (to_pos == string::npos) return NULL;
    size_t from_pos = buf.find_first_of("\r\n", to_pos + 2);
    if (from_pos == string::npos) return NULL;
    message_t* res = new message_t;
    res->to.assign(buf, 0, to_pos);
    res->from.assign(buf, to_pos + 2, from_pos - to_pos - 2);
    res->chat_text.assign(buf, from_pos + 2, len - from_pos - 2);
    return res;
}

void Codec::OnMessage(Connection* conn, ChannelBuffer* buf, Timestamp time) {
    while (buf->ReadableBytes() >= kMinMsgSize) {
        size_t msg_len = buf->PeekInt32();
        // msg too long, discard
        if (msg_len > 2046) {
            err_handler_(conn, time);
        }
        size_t head_len = 4;
        if (buf->ReadableBytes() >= msg_len + head_len) {
            std::auto_ptr<message_t> msg(Parse(buf->Peek() + head_len, msg_len));
            buf->Retrieve(msg_len + head_len);
            if (msg.get() == NULL/* && err_handler_*/) {
                err_handler_(conn, time);
                buf->RetrieveAll();
            }
            else {
                msg_handler_(conn, msg.get(), time);
            }
        }
        else {
            break;
        }
    }
}

void Codec::SendMessage(Connection* conn, const message_t& message) {
    ChannelBuffer buf;
    char sep[2] = { '\r', '\n'};
    buf.Append(message.to.data(), message.to.size());
    buf.Append(sep, sizeof sep);
    buf.Append(message.from.data(), message.from.size());
    buf.Append(sep, sizeof sep);
    buf.Append(message.chat_text.data(), message.chat_text.size());
    int32_t len = net::H2N_32(static_cast<int32_t>(buf.ReadableBytes()));
    buf.Perpend(&len, sizeof len);
    conn->Send(buf);
}
